package com.github.niefy.modules.third.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.github.niefy.common.exception.ErrorMessage;
import com.github.niefy.common.utils.*;
import com.github.niefy.modules.third.dto.in.ThirdUserIn_001;
import com.github.niefy.modules.third.dto.in.ThirdUserIn_002;
import com.github.niefy.modules.third.dto.out.ThirdUserOut_001;
import com.github.niefy.modules.third.dto.out.ThirdUserOut_002;
import com.github.niefy.modules.third.entity.ThirdUser;
import com.github.niefy.modules.third.enums.SpmApiidEnum;
import com.github.niefy.modules.third.enums.SecApiURLEnum;
import com.github.niefy.modules.third.form.ThirdUserForm;
import com.github.niefy.modules.third.service.ThirdUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 微信网页授权相关
 */
@RestController
@RequestMapping("/third/thirdUser")
@Tag(name = "第三方用户操作")
@RequiredArgsConstructor
@Slf4j
public class ThirdUserController {

    private final WxMpService wxMpService;
    private final ThirdUserService thirdUserService;

    @Value("${sec-api.auth.domain}")
    String authDomain;
    @Value("${sec-api.spm.accesskey}")
    String authAccessKey;
    @Value("${sec-api.spm.domain}")
    String spmDomain;
    @Value("${sec-api.spm.accesskey}")
    String spmAccessKey;

    /*前置颁发token，
       首页自动发送请求,若客户端本地没有thirdId-token,则token已过期或首次登录,根据code换取到用户openid后将用户信息放到thirdToken存入cookie
    *   使用openid查询third_user,有绑定关系,则颁发一个thirdId存入cookie
    * */
    @PostMapping("/getThirdToken")
    @Operation(summary = "获取thirdToken",description = "用户授权跳转登录页后自动调用,若库中已有绑定关系则同时颁发thirdId-token")
    public R getThirdToken(HttpServletRequest request, HttpServletResponse response,
                           @RequestBody ThirdUserForm form, @CookieValue(name="thirdId", required=false) String thirdId,
                           @CookieValue(name="thirdToken", required=false) String thirdTokenCookie) {
        //非首次登录且token未过期
        if (thirdId!=null){
            return R.redirect("token未过期跳转至菜单页");
        }
        //解绑后马上回到首页再次登录，将只携带thirdToken,直接返回
        if (thirdTokenCookie!=null){
            return R.ok();
        }
        try {
            //可作为场景值，具体看业务了,要支持多公众号的话可以将state设为appid
            String state = form.getState();
            this.wxMpService.switchoverTo(state);
            //换取微信用户信息
            WxOAuth2AccessToken token = wxMpService.getOAuth2Service().getAccessToken(form.getCode());
            WxOAuth2UserInfo userInfo = wxMpService.getOAuth2Service().getUserInfo(token,"zh_CN");
            String openId = userInfo.getOpenid();

            //微信用户信息token
            Map<String, Object> userInfoMap = BeanUtil.beanToMap(userInfo, null);
            userInfoMap.put("appId", state);
            String thirdToken = JWTUtils.getToken(userInfoMap);
            CookieUtil.setThirdCookie(response, "thirdToken", thirdToken);
            //存在绑定关系则为同一微信二次登录
            ThirdUser thirdUser = thirdUserService.query().eq("open_id", openId).one();
            if (thirdUser!=null){
                CookieUtil.setThirdCookie(response, "thirdId", JWTUtils.getToken("thirdId", thirdUser.getThirdId()));
                return R.redirect("非首次登录已绑定,重生成token跳转至菜单页");
            }
            return R.ok();
        } catch (WxErrorException e) {
            log.error("code换取用户信息失败", e);
            return R.error(e.getError().getErrorMsg());
        }
    }

    /**
     * getThirdIdToken返回R.code为301即为二次登录,直接跳转功能菜单页面,
     若没有则为首次登录,用户在首页输入thirdId\pwd,调用该接口,调用CAS接口校验学号密码后绑定关系入库
     颁发thirdId-token,前端跳转功能菜单页面
     */
    @PostMapping("/getThirdIdToken")
    @Operation(summary = "获取thirdIdToken",description = "用户首次登录点击登录按钮时调用")
    public R getThirdIdToken(HttpServletRequest request, HttpServletResponse response,
                             @RequestBody ThirdUserForm form) {
        //拼装请求地址
        String url = authDomain + SecApiURLEnum.SPM_CALL;
        //调用CAS接口校验学号密码
        //构造加密认证请求参数 accesskey
        SymmetricCrypto des = new SymmetricCrypto(SymmetricAlgorithm.DES, AtypicalUtil.getKey(authAccessKey.getBytes(StandardCharsets.UTF_8)));
        var encryptedwd = des.encryptHex(form.getPassword().getBytes());
        var thirdUserLoginOut = ThirdUserOut_001.builder()
                .userid(form.getThirdId())
                .encryptedwd(encryptedwd)
                .build();
        thirdUserLoginOut.createSign(SpmApiidEnum.verify_user_encrypted_auth.name(), "SPMSTU",
                "userid", authAccessKey, form.getThirdId());
        try {
            ThirdUserIn_001 in = HttpClientUtil.http2GetJsonSync(url, ThirdUserIn_001.class, thirdUserLoginOut);
            if (!ThirdUserIn_001.VerifyFlag.SUCCESS.getValue().equals(in.getVerifyflag())) {
                return R.error("密码错误或学号不存在");
            }
            //微信登录，修改记录以新openId建立新的绑定关系，若不存在记录，则将新的绑定关系落库
            var userinfo = in.getUserinfo();
            var thirdUser = ThirdUser.builder()
                    .openId((String) request.getAttribute("openid"))
                    .thirdId(form.getThirdId())
                    .appId((String) request.getAttribute("appId"))
                    .userName(userinfo.getUsername())
                    .roleId(userinfo.getRoleid())
                    .roleName(userinfo.getRolename())
                    .departNo(userinfo.getDepartno())
                    .departName(userinfo.getDepartname())
                    .consuserId(userinfo.getConsuserid())
                    .updateTime(LocalDateTime.now()).build();
            thirdUserService.saveOrUpdateWithValid(thirdUser);
            //颁发thirdId-token
            CookieUtil.setThirdCookie(response, "thirdId", JWTUtils.getToken("thirdId", thirdUser.getThirdId()));
        } catch (Exception e) { //IOException | InterruptedException e
            log.error("用户身份验证绑定失败", e);
            return ErrorMessage.messageToR("A0157");
        }
        return R.redirect("微信与学号绑定，跳转菜单页");
    }

    /**
     * 用户解绑*/
    @Transactional
    @DeleteMapping("/unbind")
    @Operation(summary = "学号与微信用户解绑",description = "用户点击")
    public R unbind(HttpServletRequest request, HttpServletResponse response) {
        String thirdId = (String) request.getAttribute("thirdId");
        thirdUserService.removeById(thirdId);
        CookieUtil.clearCookie(response,"thirdId");
        return R.ok();
    }

    /**
     * 获取用户详细信息
     * @param request
     * @param response
     * @param thirdId
     * @return
     */
    @GetMapping("/userDetailInfo")
    @Operation(summary = "获取用户详细信息",description = "获取用户详细信息")
    public R getUserDetailInfo(HttpServletRequest request, HttpServletResponse response,
                               @RequestAttribute String thirdId){

        var thirdUserOut002 = ThirdUserOut_002.builder()
                .studentno(thirdId)
                .build();
        thirdUserOut002.createSign(SpmApiidEnum.querystudentinfo.name(),"SPMSTU","studentno",
                spmAccessKey,thirdId);
        //拼装请求地址
        String url = spmDomain + SecApiURLEnum.SPM_CALL;
        ThirdUserIn_002 in = HttpClientUtil.http2GetJsonSync(url, ThirdUserIn_002.class, thirdUserOut002);
        return R.ok().put("studentinfo",in.getStudentinfo());
    }
}
